package com.zhiwang.planet.mvvm.model

import android.content.res.Resources
import android.os.Build
import android.util.Log
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.zhiwang.planet.BuildConfig
import com.zhiwang.planet.exception.ExpiredException
import com.zhiwang.planet.exception.XQException
import com.zhiwang.planet.tsl.TslProvider
import com.zhiwang.planet.util.ChannelUtil
import com.zhiwang.planet.util.TokenManager
import io.reactivex.Observable
import io.reactivex.ObservableSource
import io.reactivex.Observer
import io.reactivex.functions.Function
import io.reactivex.schedulers.Schedulers
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import okhttp3.ResponseBody
import okhttp3.logging.HttpLoggingInterceptor
import okio.Buffer
import okio.GzipSource
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.net.Proxy
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSession

class RestApiHelper @Inject constructor(var gson: Gson, var tslProvider: TslProvider) {


    fun create(): RestApi {
        return create(BuildConfig.BASE_URL)
    }


    fun create(baseUrl: String): RestApi {
        val httpLoggingInterceptor = HttpLoggingInterceptor(OkHttpLogger())
//        val cookieManager = CookieManager()
//        cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL)
        httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
        val builder = OkHttpClient.Builder()
        builder
                .addInterceptor {
                    it.proceed(it.request().newBuilder()
                            .addHeader("apptoken", TokenManager.token())
                            .addHeader("appdevice", "android")
                            .addHeader("plat", ChannelUtil.getChannel())
                            .addHeader("appversion", BuildConfig.VERSION_CODE.toString())
                            .addHeader("device-density", Resources.getSystem().displayMetrics.density.toString())
                            .addHeader("model", Build.MODEL)
                            .addHeader("versionname", BuildConfig.VERSION_NAME)
                            .addHeader("os_version", Build.VERSION.RELEASE)
                            .addHeader("mtype", "xiangguo")
                            .build())
                }
                //不走代理，避免有些用户的设备上挂载了代理，抓取应用信息或者是因为代理网络的原因导致不必要的问题
                // （当然这样并不能完全阻止抓包）
                .proxy(Proxy.NO_PROXY)
        if (BuildConfig.DEBUG) {
            builder.addNetworkInterceptor(httpLoggingInterceptor)
//            builder.addNetworkInterceptor(RestApiResponseInterceptor)
        }

        val okClient = builder
                .connectTimeout(10000L, TimeUnit.MILLISECONDS)
                .retryOnConnectionFailure(true)
                .sslSocketFactory(tslProvider.sslSocketFactory, tslProvider.trustManager)
                .hostnameVerifier(object : HostnameVerifier {
                    override fun verify(hostname: String?, session: SSLSession?): Boolean {
                        return true
                    }

                })
                .build()
        val restApi = Retrofit.Builder()
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(gson))
                .client(okClient)
                .baseUrl(baseUrl)
                .build().create(RestApi::class.java)

//        val cacheWrapperRestApi = java.lang.reflect.Proxy.newProxyInstance(restApi::class.java.classLoader, arrayOf(restApi::class.java), object : InvocationHandler {
//
//            override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any {
//
//            }
//
//        })
        return createWrapperService(RestApi::class.java, restApi)
//        return  restApi

    }


    /**
     * 尽量把 retrofit初始化的部分放到子线程
     */
    private fun <T> createWrapperService(clazz: Class<T>, restApi: RestApi): T {

        return java.lang.reflect.Proxy.newProxyInstance(clazz.classLoader, arrayOf(clazz), object : InvocationHandler {

            override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {

                val returnType = method?.genericReturnType

                if (returnType == Observable::class.java) {

                    return Observable.defer {

                        val ob = method.invoke(restApi, *(args ?: arrayOfNulls(0))) as Observable<*>

                        return@defer ob.subscribeOn(Schedulers.io())
                    }.subscribeOn(Schedulers.single())
                }

                return method?.invoke(restApi, *(args ?: arrayOfNulls(0)))
            }

        }) as T

    }


    fun testCreate(): RestApi {
        return create(BuildConfig.DEBUG_TEST_BASE_URL)
    }
}


class ResponseFunc<T> : Function<ResponseWrapper<T>, ObservableSource<T>> {
    override fun apply(t: ResponseWrapper<T>): ObservableSource<T> {
        return ObservableSource { it: Observer<in T> ->
            when {
                t.isOk() -> it.onNext(t.data)
                t.isAuthExpired() -> it.onError(ExpiredException(t.errMsg))
                else -> it.onError(Throwable(t.errMsg))
            }
        }
    }

}

class LatestResponse<T, R>(@SerializedName("news") var news: List<T>,
                           @SerializedName("date") var date: String,
                           @SerializedName("top_stories") var topStories: List<R>,
                           @SerializedName("is_today") var today: Boolean)


class Response<T>(@SerializedName("date") var date: String,
                  @SerializedName("news") var news: List<T>
)

//解析格式
class ResponseWrapper<T>(@SerializedName("status") val errCode: Int = -100,
                         @SerializedName("msg") val errMsg: String,
                         @SerializedName("data") val data: T) {
    companion object {
        @JvmStatic
        fun create(message: String): ResponseWrapper<String> {

            return ResponseWrapper(-12345, message, "~~~~data fetch error~~~")
        }

        const val AUTH_EXPIRED_CODE = -99
    }

    fun isOk() = errCode == 0

    fun isAuthExpired() = errCode == AUTH_EXPIRED_CODE


    fun error(): Throwable {
        if (isAuthExpired())
            return ExpiredException(errMsg)
        else return XQException(errMsg)
    }
}

//针对各种共性很高的 list 数据

class ListReponseWrapper<T>(@SerializedName("total")
                            var total: Int,
                            @SerializedName("page")
                            var currPage: Int,
                            @SerializedName("list")
                            var dataList: List<T>,
                            @SerializedName("hasmore")
                            var hasMore: Int
)


abstract class ResponseBodyInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val url = request.url().toString()
        val response = chain.proceed(request)
        response.body()?.let { responseBody ->
            val contentLength = responseBody.contentLength()
            val source = responseBody.source()
            source.request(Long.MAX_VALUE)
            var buffer = source.buffer()

            if ("gzip".equals(response.header("Content-Encoding"), ignoreCase = true)) {
                GzipSource(buffer.clone()).use { gzippedResponseBody ->
                    buffer = Buffer()
                    buffer.writeAll(gzippedResponseBody)
                }
            }

            val contentType = responseBody.contentType()
            val charset: Charset = contentType?.charset(StandardCharsets.UTF_8)
                    ?: StandardCharsets.UTF_8
            if (contentLength != 0L) {
                return intercept(response, url, buffer.clone().readString(charset))
            }
        }
        return response
    }

    abstract fun intercept(response: Response, url: String, body: String): Response


}


object RestApiResponseInterceptor : ResponseBodyInterceptor() {


    //todo 模拟账户明细请求
    val accountListResponseData =
            "{\"status\":0,\"msg\":\"success\",\"data\":{\"total\":\"8\",\"list\":[{\"type\":\"8\",\"number\":\"-500000\",\"cost_text\":\"\\u7533\\u8bf7\\u63d0\\u73b050\\u5143, \\u624b\\u7eed\\u8d391\\u5143, \\u5b9e\\u9645\\u5230\\u8d2649\\u5143\",\"date\":\"2019-09-18 15:04:32\"},{\"type\":\"8\",\"number\":\"-500000\",\"cost_text\":\"\\u7533\\u8bf7\\u63d0\\u73b050\\u5143, \\u624b\\u7eed\\u8d391\\u5143, \\u5b9e\\u9645\\u5230\\u8d2649\\u5143\",\"date\":\"2019-09-18 15:04:32\"},{\"type\":\"8\",\"number\":\"-500000\",\"cost_text\":\"\\u7533\\u8bf7\\u63d0\\u73b050\\u5143, \\u624b\\u7eed\\u8d391\\u5143, \\u5b9e\\u9645\\u5230\\u8d2649\\u5143\",\"date\":\"2019-09-18 15:04:30\"},{\"type\":\"2\",\"number\":\"30000\",\"cost_text\":\"\\u9080\\u8bf7\\u5956\\u52b1(3\\u4eba)\",\"date\":\"2019-09-12 11:47:08\"},{\"type\":\"2\",\"number\":\"30000\",\"cost_text\":\"\\u9080\\u8bf7\\u5956\\u52b1(3\\u4eba)\",\"date\":\"2019-09-12 11:43:41\"},{\"type\":\"2\",\"number\":\"30000\",\"cost_text\":\"\\u9080\\u8bf7\\u5956\\u52b1(3\\u4eba)\",\"date\":\"2019-09-11 11:43:37\"},{\"type\":\"2\",\"number\":\"30000\",\"cost_text\":\"\\u9080\\u8bf7\\u5956\\u52b1(3\\u4eba)\",\"date\":\"2019-09-10 11:43:34\"},{\"type\":\"2003\",\"number\":\"10000\",\"cost_text\":\"\\u9080\\u8bf7\\u597d\\u53cb\",\"date\":\"2019-09-09 18:05:36\"}],\"page\":\"1\",\"hasmore\":0}}"

    val TAG = "ResponseInterceptor"
    override fun intercept(response: Response, url: String, body: String): Response {


//        if (url.contains("ucenter/goldz_list"))
//            return response.newBuilder()
//                .body(ResponseBody.create(response.body()?.contentType(), accountListResponseData)).build()

        //处理 ucenter/my_info 接口的错误

        Log.i(TAG, "intercept:$url  -->response:\n $body")

        val errIndex = body.indexOf("{\"status\"")

        if (errIndex <= 0) return response

        val responseString = body.substring(errIndex)

//        if (BuildConfig.DEBUG)
//            Log.i(TAG, "intercept result: $responseString")

        return response.newBuilder()
                .code(200)
                .body(ResponseBody.create(response.body()?.contentType(), responseString)).build()
    }


}

